﻿using IOP.ISEA;
using IOP.ISEA.OBJ;
using IOP.Models;
using IOP.SgrA;
using IOP.SgrA.SilkNet.Vulkan;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace VkSample73
{
    class Program
    {
        static async Task Main(string[] args)
        {
            await CreateISEA(true);
            var h = Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((config) =>
                {
                    config.SetBasePath(Directory.GetCurrentDirectory());
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) config.AddVulkanJsonFile("Vulkan.json");
                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) config.AddVulkanJsonFile("Vulkan-Linux.json");
                    else throw new NotSupportedException("Not supported Platform");
                })
                .ConfigureSgrAHost((sgra) =>
                {
                    sgra.AddVulkanEnvironmental();
                    sgra.UseStartup<Startup>();
                }).ConfigureLogging((logger) => logger.AddConsole()); ;
            await h.RunConsoleAsync();
        }

        static async Task CreateISEA(bool isNeed)
        {
            if (!isNeed) return;
            var basePath = AppContext.BaseDirectory;
            var modelsPath = Path.Combine(basePath, "Assets", "Models");
            OBJDocument obj = await OBJDocument.LoadAsync(Path.Combine(modelsPath, "ch.obj"));
            float[] result = BuildVectexBuffer(obj.Vectices.GetAllVectices(), obj.Groups[0].Face.GetAllFaces());
            ISEADocument isea = new ISEADocument();
            MeshNode mesh = isea.CreateEmptyMeshNode("CHMesh", obj.Groups[0].Face.Count)
                .AddMeshData(result, 24, 0, MeshDataPurpose.COMPLEX, true);
            isea.CreateRenderObjectNode("CH", mesh);
            await isea.Save(basePath, "test73");
            obj.Dispose();
        }

        static float[] BuildVectexBuffer(Vector3[] vectices, IndexVector3[] faces)
        {
            Span<Vector3> local = new Vector3[faces.Length * 2];
            Dictionary<int, List<Vector3>> normalsDic = new Dictionary<int, List<Vector3>>();
            Dictionary<int, Vector3> normals = new Dictionary<int, Vector3>();
            Vector3 x0, x1, x2, vxa, vxb, normal;
            int f1, f2, f3;
            List<Vector3> l;
            for (int i = 0; i < faces.Length; i += 3)
            {
                f1 = faces[i].X - 1;
                f2 = faces[i + 1].X - 1;
                f3 = faces[i + 2].X - 1;
                x0 = vectices[f1];
                x1 = vectices[f2];
                x2 = vectices[f3];
                vxa = x1 - x0;
                vxb = x2 - x0;
                normal = Vector3.Normalize(Vector3.Cross(vxa, vxb));
                if (normalsDic.TryGetValue(f1, out l)) l.Add(normal);
                else normalsDic.Add(f1, new List<Vector3>() { normal });
                if (normalsDic.TryGetValue(f2, out l)) l.Add(normal);
                else normalsDic.Add(f2, new List<Vector3>() { normal });
                if (normalsDic.TryGetValue(f3, out l)) l.Add(normal);
                else normalsDic.Add(f3, new List<Vector3>() { normal });
            }
            normal = Vector3.Zero;
            foreach (var item in normalsDic)
            {
                if (!normals.ContainsKey(item.Key))
                {
                    foreach (var n in item.Value) normal += n;
                    normal = Vector3.Normalize(normal);
                    normals.Add(item.Key, normal);
                }
            }
            int index = 0, f;
            for (int i = 0; i < faces.Length; i++)
            {
                f = faces[i].X - 1;
                local[index++] = vectices[f];
                local[index++] = normals[f];
            }
            Span<float> result = MemoryMarshal.Cast<Vector3, float>(local);
            return result.ToArray();
        }
    }
}
